%{
#include <stdio.h>
#include <stdlib.h>
#include "latex_math.h"

int yyparse(void);
int yylex(void);

int eof = 0;

void yyerror(const char * err)
{
	fprintf(stderr,"error: %s\n",err);
	exit(1);
}

void yywarn(const char* err)
{
	printf("warning: %s\n",err);
}

int yywrap()
{
	return 1;
}

int main()
{
		yyparse();
		return 0;
}

%}

%token <decimal> DIGIT
%token DECIMAL

%token NL T_EOF

%token OP_PLUS OP_MINUS
%token OP_TIMES OP_DIV
%token OP_EXP OP_FACT
%token OP_LPAREN OP_RPAREN OP_BAR

%token CONST_PI
%token CONST_E
%token CONST_PHI

%token L_LBRACE L_RBRACE
%token L_FRAC L_BINOM

%token M_COMMA

%type <decimal> expr expr2 expr2a expr2b expr3 group integer float fracint
%type <decimal_list> multinomial_list

%expect 4

%union
{
	int integer;
	double decimal;
	int* integer_list;
	double* decimal_list;
}


%%

line	: line expr NL { printf("%g\n",$2); }
			| line T_EOF { exit(0); }
			| NL
			|
			;

expr	: expr OP_PLUS expr2 { $$ = $1 + $3; }
			| expr OP_MINUS expr2 { $$ = $1 - $3; }
			| expr2 { $$ = $1; }
			;

expr2	: expr2 OP_TIMES expr2a { $$ = $1 * $3; }
			| expr2 expr2b { $$ = $1 * $2; }
			| expr2 OP_DIV expr2a { 
                if($3 == 0)
                  yyerror("division by 0");
				$$ = $1 / $3; 
            }
			| expr2a
			;

expr2a : OP_MINUS expr2b { $$ = -$2; }
			 | expr2b {$$ = $1;}

expr2b : expr2b OP_EXP group { $$ = expo($1,$3); }
			 | expr3 OP_FACT { $$ = factorial($1); }
			 | expr3 { $$ = $1; }
			 ;

expr3 : OP_LPAREN expr OP_RPAREN { $$ = $2; }
			| L_FRAC group group { 
                if($3 == 0)
                  yyerror("division by 0");
                $$ = $2/$3; }
				
			| L_BINOM group group {$$ = binom($2,$3); }
			| L_BINOM group L_LBRACE multinomial_list L_RBRACE {
              
              $$ = (double)multinom($2,$4); }
			| float
            | integer
			| CONST_PI {$$ = 3.14159265;}
			| CONST_E {$$ = 2.71828183;}
			| CONST_PHI {$$ = 1.61803399;}
			;

float      :   integer DECIMAL { $$ = $1; }
           |   integer DECIMAL fracint { $$ = $1 + $3; }
           |   DECIMAL fracint  {$$ = $2;}
           ;

fracint     :  DIGIT fracint {$$ = $1/10 + $2/10;}
            |  DIGIT    {$$ = $1/10;}
  

integer	    : integer DIGIT	{ $$ = 10*$1 + $2; }
			| DIGIT
			;

group		: L_LBRACE expr L_RBRACE { $$ = $2; }
				| DIGIT { $$ = $1; }
				;

multinomial_list	: multinomial_list M_COMMA expr
									{
										int size = $1[0] + 1;
										double* list = (double*)malloc(sizeof(double)*(size+1));
										for(int i = 0;i<size;i++)
											list[i] = $1[i];
										list[0]++;
										list[size] = $3;
										$$ = list;
										free($1);
									}
									| expr M_COMMA expr 
									{
										double* list = (double*)malloc(sizeof(double)*3);
										list[0] = 2;
										list[1] = $1;
										list[2] = $3;
										$$ = list;
									}
									;

%%
